
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/*class represents the vector of bits*/
public class BitVector implements Cloneable {

    private byte bits[];
    private int bitsNum;
    private int fitness;
    private Random r = new Random();

    /*recheives the number of bits and initializes
    the minimal meeded number of bytes accordingly*/
    public BitVector(int bitsNum) {
        bits = new byte[1 + (bitsNum >> 3)];
        this.bitsNum = bitsNum;
        initRandomBits();
        calcFitness();
    }

    public BitVector(int bitsNum, boolean isHC) {
        bits = new byte[1 + (bitsNum >> 3)];
        this.bitsNum = bitsNum;
        initRandomBits();
        calcFitness();
    }

    @Override
    public Object clone() {
        BitVector o = null;
        try {
            o = (BitVector) super.clone();
        } catch (CloneNotSupportedException e) {
            // This should never happen: It's Cloneable already!
            throw new RuntimeException(e);
        }
        o.bits = (byte[]) bits.clone();
        o.bitsNum = bitsNum;
        o.fitness = fitness;
        o.r = r;
        return o;
    }

    // set the bit in the index to 1
    public void set1(int index) {
        bits[index >> 3] |= 1 << (index & 7);
    }
    // set the bit in the index to 0

    public void set0(int index) {
        bits[index >> 3] &= ~(1 << (index & 7));
    }

    /*set a bit to 0 ot 1 according to input*/
    public void setBit(int index, int b) {
        if (b == 1) {
            set1(index);
        } else {
            set0(index);
        }
    }

    /*switch a bit at index*/
    public void switchBit(int index) throws IllegalAccessException {
        if (get(index) != 1) {
            set1(index);
        } else {
            set0(index);
        }
    }

    // returns the bit in the index
    public int get(int index) throws IllegalAccessException {
        if (index < bitsNum) {
            if ((bits[index >> 3] & 1 << (index & 7)) != 0) {
                return 1;
            }
            return 0;
        } else {
            throw new IllegalAccessException();
        }
    }

    /*changes an entire byte*/
    public void setByte(int index, byte b) {
        bits[index] = b;
    }

    /*returns an entire byte*/
    public byte getByte(int index) {
        return bits[index];
    }

    /*returns the number of bits represented in the vector*/
    public int getBitsNum() {
        return bitsNum;
    }

    /*gets 2 vectors and a cut point and crosses them
    to create 2 new vectors [AB]+[CD] => [AD],[CB]*/
    public static BitVector[] singlePointCrossOver(BitVector a, BitVector b, int index) throws IllegalAccessException {
        int size = a.getBitsNum();
        BitVector sons[] = new BitVector[2];
        sons[0] = new BitVector(size);
        sons[1] = new BitVector(size);
        for (int i = 0; i < index; i++) {
            sons[0].setBit(i, a.get(i));
            sons[1].setBit(i, b.get(i));
        }
        for (int i = index; i < size; i++) {
            sons[0].setBit(i, b.get(i));
            sons[1].setBit(i, a.get(i));
        }
        return sons;
    }

    public void mutateBit() {
        try {
            int location = r.nextInt(bitsNum);
            int b = get(location);
            if (b == 1) {
                set0(location);
            } else {
                set1(location);
            }
        } catch (IllegalAccessException ex) {
            Logger.getLogger(BitVector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void calcFitness() {
        try {
            fitness = AIUI.eq.countSatisfiedClauses(this);
            //System.out.println("Fitness: "+fitness + ", " + this.toString());
        } catch (IllegalAccessException ex) {
            Logger.getLogger(BitVector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int getFitness() {
        return fitness;
    }

    // initialize random bits to the array;
    public void initRandomBits() {
        for (int i = 0; i < bitsNum; i++) {
            setBit(i, r.nextInt(2));
        }
    }

    // so we can print the BitVector object
    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        try {
            for (int i = 0; i < bitsNum; i++) {
                ret.append(get(i) + "");
            }
        } catch (IllegalAccessException ex) {
            Logger.getLogger(BitVector.class.getName()).log(Level.SEVERE, null, ex);
        }

        return ret.toString();
    }
}
